package com.hp.ecs.xqa.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;

public class ReflectionUtil {
	public static void setField(Class clazz, String fieldName, Object value) {
		try {
			Field field = clazz.getDeclaredField(fieldName);
			field.setAccessible(true);
			field.set(null, value);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public static void setField(Object object, String fieldName, Object value) {
		try {
			Field field = object.getClass().getDeclaredField(fieldName);
			field.setAccessible(true);
			field.set(object, value);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public static Object getField(Object object, String fieldName) {

		Field field = getField(object.getClass(), fieldName);
		if (field == null) {
			throw new IllegalArgumentException("No field:" + fieldName + " in " + object.getClass().getName());
		}
		try {
			field.setAccessible(true);
			return field.get(object);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public static Field getField(Class clazz, String fieldName) {
		for (Field field : clazz.getDeclaredFields()) {
			if (field.getName().equals(fieldName)) {
				return field;
			}
		}
		if (clazz.equals(Object.class)) {
			return null;
		}
		return getField(clazz.getSuperclass(), fieldName);
	}

	public static Method getMethod(Class clazz, String methodName) {
		for (Method m : clazz.getMethods()) {
			if (m.getName().equals(methodName)) {
				return m;
			}
		}
		if (clazz.equals(Object.class)) {
			return null;
		}
		return getMethod(clazz.getSuperclass(), methodName);
	}

	public static Method getSetter(Method getter) {
		final String name = getter.getName();
		if (getter.getParameterTypes().length > 0 || !(name.startsWith("get") || name.startsWith("is"))) {
			return null;
		}
		String setter = "set" + name.substring(name.startsWith("get") ? 3 : 2);
		try {
			return getter.getDeclaringClass().getMethod(setter, getter.getReturnType());
		} catch (NoSuchMethodException e) {
			return null;
		}
	}

	public static Method getSetter(String field, Class clazz) {
		String upField = StringUtil.capitalize(field);
		for (Method m : clazz.getMethods()) {
			if (m.getName().endsWith(upField)) {
				Method setter = getSetter(m);
				if (setter != null) {
					return setter;
				}
			}
		}
		return null;
	}

	/**
	 * 获取方法参数名称，按给定的参数类型匹配方法
	 * 
	 * @param clazz
	 * @param method
	 * @param paramTypes
	 * @return
	 */
	public static String[] getMethodParamNames(Class clazz, String method, Class... paramTypes) {
		try {
			ClassPool pool = ClassPool.getDefault();
			CtClass cc = pool.get(clazz.getName());
			String[] paramTypeNames = new String[paramTypes.length];
			for (int i = 0; i < paramTypes.length; i++)
				paramTypeNames[i] = paramTypes[i].getName();
			CtMethod cm = cc.getDeclaredMethod(method, pool.get(paramTypeNames));
			return getMethodParamNames(cm);
		} catch (NotFoundException e) {
			throw new IllegalStateException(e);
		}
	}

	protected static String[] getMethodParamNames(CtMethod cm) throws NotFoundException {
		CtClass cc = cm.getDeclaringClass();
		MethodInfo methodInfo = cm.getMethodInfo();
		CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
		LocalVariableAttribute attr = (LocalVariableAttribute)codeAttribute.getAttribute(LocalVariableAttribute.tag);
		if (attr == null){
			throw new IllegalArgumentException("Unkown methods:" + cc.getName());
		}

		String[] paramNames = new String[cm.getParameterTypes().length];
		int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
		for (int i = 0; i < paramNames.length; i++){
			paramNames[i] = attr.variableName(i + pos);
		}
		return paramNames;
	}
}
